#include "MyString.h"
using namespace std;

#pragma region toolfuncs
int MyStrLen(const char *_pstr)
{
    int _len = 0;
    while (*_pstr) {
        ++_len;
        ++_pstr;
    }
    return _len;
}

void MyStrCpy(char* _dst, const char* _src, int n = -1){
    if (n == -1) {
        while (*_src) {
            *(_dst++) = *(_src++);
        }
        *_dst = '\0';
    }
    else {
        while (n-- > 0) {
            *(_dst++) = *(_src++);
        }
    }
}
#pragma endregion

#pragma region constructors
MyString::MyString()
{
    _pstr = nullptr;
    _len = 0;
    _size = 0;
}

MyString::MyString(const char* str) {
    _len = MyStrLen(str);
    _size = (_len / 8 + 1) * 8;
    _pstr = new char[_size];
    _len = sprintf(_pstr, "%s", str);
}

MyString::MyString(const char* str, int len) {
    _len = len;
    _size = (_len / 8 + 1) * 8;
    _pstr = new char[_size];
    _len = sprintf(_pstr, "%s", str);
}

MyString::MyString(const int i)
{
    _size = 16;
    _pstr = new char[_size];
    _len = sprintf(_pstr, "%d", i);
}

MyString::MyString(const long int li)
{
    _size = 16;
    _pstr = new char[_size];
    _len = sprintf(_pstr, "%ld", li);
}

MyString::MyString(const long long int lli)
{
    _size = 24;
    _pstr = new char[_size];
    _len = sprintf(_pstr, "%lld", lli);
}

MyString::MyString(const float f, const unsigned int decimalLen)
{   
    char format[5];
    // float only has 6 reliable decimal
    sprintf(format, "%%.%df", decimalLen % 6);

    _size = 24;
    _pstr = new char[_size];
    _len = sprintf(_pstr, (const char*)format , f);
}

MyString::MyString(const double d, const unsigned int decimalLen)
{
    char format[6];
    // double only has 16 reliable decimal
    sprintf(format, "%%.%df", decimalLen % 16);

    _size = 32;
    _pstr = new char[_size];
    _len = sprintf(_pstr, (const char*)format, d);
}

MyString::MyString(const MyString& mystr) {
    _size = mystr._size;
    _len = mystr._len;
    _pstr = new char[_size];
    sprintf(_pstr, "%s", mystr._pstr);
}
#pragma endregion

int MyString::len() const {
    return _len;
}

int MyString::size() const {
    return _size;
}

bool MyString::isEmpty() {
    return _len == 0;
}

int MyString::toInt() {
    /*
        Only deal with number within the range of int,
        omit numbers and characters following the valid int part.
        Examples:
            input           T/F         output          explanation
            +++---+-+-123     valid     -123            long leading + and -
            +2147483647       valid     2147483647      int_max
            -2147483648       valid     -2147483648     int_min
            2147483648        valid     214748364       too big for int, omit 8
            +a              invalid     exception       a can't be convert to int
            +-123abc          valid     -123            omit following invalid characters, i.e. abc
    */
    int ret = 0;
    bool neg = false;
    const char* pstr = _pstr;
    const int int_max = 0x7fffffff;
    const int int_min = 1 << 31;

    // Deal with leading + and -
    while (*pstr) {
        if ('-' == *pstr) {
            neg = !neg;
            ++pstr;
        }
        else if ('+' == *pstr) {
            ++pstr;
        }
        else if (*pstr <= '9' && *pstr >= '0') {
            break;
        }
        else {
            throw convert_error(_pstr, "int");
        }
    }

    // Deal with number part
    while (*pstr <= '9' && *pstr >= '0') {
        if (ret < int_max / 10) {
            ret = ret * 10 + (*pstr - '0');
        }
        // Deal with int_max and int_min
        else if (ret == int_max / 10) {
            if (*pstr == '9') {
                return neg ? -ret : ret;
            }
            else if (neg && *pstr == '8') {
                return int_min;
            }
            else {
                ret = ret * 10 + (*pstr - '0');
                return neg ? -ret : ret;
            }
        }
        // Current ret is already bigger than int_max / 10
        else {
            return neg ? -ret : ret;
        }
        ++pstr;
    }
    return neg ? -ret : ret;
}

float MyString::toFloat() {
    float retf = -2.0f;
    bool cnt = sscanf(_pstr, "%f", &retf);
    if (!cnt) {
        throw convert_error(_pstr, "float");
    }
    return retf;
}

MyString MyString::subString(const int beg, const int len) {
    if (beg >= _len) return MyString();
    if (beg + len - 1 <= _len) {
        char* str = new char[len + 1];
        MyStrCpy(str, _pstr + beg, len);
        str[len] = '\0';
        return MyString(str, len);
    }
    else {
        char* str = new char[_len - beg + 1];
        MyStrCpy(str, _pstr + beg, _len - beg);
        str[_len - beg] = '\0';
        return MyString(str, _len - beg);
    }
}

int MyString::find(const MyString& pattern) {
    int* next;
    {
        next = new int[pattern._len];
        next[0] = -1;
        int j = 0;
        int k = -1;
        while (j < pattern._len - 1) {
            if (k == -1 || pattern._pstr[k] == pattern._pstr[j]) {
                next[++j] = ++k;
            }
            else {
                k = next[k];
            }
        }
    }

    int i = 0;
    int j = 0;
    while (i < this->_len && j < pattern._len) {
        if (j == -1 || this->_pstr[i] == pattern._pstr[j]) {
            ++i;
            ++j;
        }
        else {
            j = next[j];
        }
    }

    if (j == pattern._len) {
        return i - j;
    }

    return -1;
}

char* MyString::toCString(const int, const int) {
    return nullptr;
}

MyString MyString::operator+(const MyString& add) {
    int totalLen = _len + add._len;
    char* pstr;
    pstr = new char[totalLen + 1];
    sprintf(pstr, "%s", _pstr);
    sprintf(pstr + _len, "%s", add._pstr);
    return MyString(pstr, totalLen);
}

MyString MyString::operator-(const MyString& minus) {
    MyString mystr(*this);
    int p = mystr.find(minus);
    while (p != -1) {
        MyStrCpy(mystr._pstr + p, mystr._pstr + p + minus._len);
        mystr._len -= minus._len;
        p = mystr.find(minus);
    }
    return mystr;
}


MyString& MyString::operator=(const MyString& mystr) {
    if (this == &mystr) return *this;
    _size = mystr._size;
    _len = mystr._len;
    _pstr = new char[_size];
    sprintf(_pstr, "%s", mystr._pstr);
    return *this;
}

MyString& MyString::operator+=(const MyString& add) {
    int totalLen = _len + add._len;
    if (_size < totalLen) {
        _size = (totalLen / 8 + 1) * 8;
        char* buffer = _pstr;
        _pstr = new char[_size];
        sprintf(_pstr, "%s", buffer);
        sprintf(_pstr + _len, "%s", add._pstr);
        _len = totalLen;
    }
    else {
        sprintf(_pstr + _len, "%s", add._pstr);
        _len = totalLen;
    }
    return *this;
}

MyString& MyString::operator-=(const MyString& minus) {
    int p = this->find(minus);
    while (p != -1) {
        MyStrCpy(_pstr + p, _pstr + p + minus._len);
        _len -= minus._len;
        p = this->find(minus);
    }
    return *this;
}

ostream& operator<<(ostream& os, const MyString& mystr) {
    if (mystr._pstr) os << mystr._pstr;
    return os;
}

MyString::~MyString() {
    if (_pstr) delete[] _pstr;
}

ostream& operator<<(ostream& os, const convert_error& ce) {
    os << '"' << ce.what() << '"' << "can't be convert to " << ce._type << endl;
    return os;
}